using System.Text;
using System.Text.RegularExpressions;
using Gateway.Application.Channels;
using Gateway.Common;
using Gateway.IApplication.RequestLog.Input;
using Gateway.Infrastructure.Provider.Operator;
using Gateway.UI.Api.Filters.Attributes;
using Microsoft.AspNetCore.Mvc.Filters;

namespace Gateway.UI.Api.Filters;

/// <summary>
/// log 过滤器
/// </summary>
public class LogFilter : Attribute, IAsyncActionFilter
{
    private readonly IOperatorProvider _operatorProvider;
    private readonly RequestLogChannel _logChannel;

    /// <summary>
    /// 
    /// </summary>
    /// <param name="operatorProvider"></param>
    /// <param name="logChannel"></param>
    public LogFilter(IOperatorProvider operatorProvider, RequestLogChannel logChannel)
    {
        _operatorProvider = operatorProvider;
        _logChannel = logChannel;
    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="context"></param>
    /// <param name="next"></param>
    public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
    {
        await next();
        var logAttribute = (LogAttribute)context.ActionDescriptor.FilterDescriptors
            .FirstOrDefault(p => p.Filter is LogAttribute)?.Filter;
        if (logAttribute != null)
        {
            var log = await BuildLog(context.HttpContext, logAttribute);
            await _logChannel.Write(log);
        }
    }

    private async Task<AddRequestLogInput> BuildLog(HttpContext context, LogAttribute logger)
    {
        string message = logger.LogEvent;
        if (logger.Regex.Count > 0)
        {
            //进行读数
            string requestInfo = await ReadRequest(context);
            if (!string.IsNullOrEmpty(requestInfo))
            {
                foreach (var item in logger.Regex)
                {
                    var regValue = Match(requestInfo, "(?<=\"" + item + "\":)[^,]*");
                    regValue = regValue.Replace("\"", "").Replace("}", "");
                    message = message.Replace("${" + item + "}", regValue);
                }
            }
        }

        var log = new AddRequestLogInput()
        {
            LogType = logger.LogType.ToString(),
            LogName = EnumHelper.GetEnumDesc(logger.LogType),
            ApiPath = context.Request.Path.Value,
            HttpMethod = context.Request.Method,
            CreatedTime = DateTime.Now,
            CreatorName = _operatorProvider.GetOperator().OperatorName,
            CreatedBy = _operatorProvider.GetOperator().OperatorId,
            Ip = _operatorProvider.GetOperator().Ip,
            Event = message
        };
        return log;
    }

    /// <summary>
    /// 读取数据
    /// </summary>
    /// <param name="context"></param>
    /// <returns></returns>
    private async Task<string> ReadRequest(HttpContext context)
    {
        var request = context.Request;

        // request.EnableBuffering(); is necessary for reading the Body stream more than once
        // You need to move the position back to the beginning, otherwise the next pice of middleware
        // that try to get the body will fail to do that, i.e. the position is in the end and thus the 
        // body seems to be empty
        request.Body.Position = 0;
        var buffer = new byte[Convert.ToInt32(request.ContentLength)];
        await request.Body.ReadAsync(buffer, 0, buffer.Length);
        var bodyAsText = Encoding.UTF8.GetString(buffer);
        return bodyAsText;
    }
    /// <summary>
    /// 
    /// </summary>
    /// <param name="s"></param>
    /// <param name="p"></param>
    /// <param name="i"></param>
    /// <returns></returns>
    public string Match(string s, string p, int i = 0)
    {
        if (string.IsNullOrEmpty(s) || string.IsNullOrEmpty(p)) return string.Empty;
        s = s.Replace(" ", "");
        s = s.Replace("\n", "");
        var match = Regex.Match(s, p, RegexOptions.IgnoreCase);
        if (i < match.Groups.Count) return match.Groups[i].Value;
        i = 0;
        return match.Groups[i].Value;
    }
}